﻿namespace Jsl.FxCop
{
    using System;
    using System.Drawing;
    using System.Windows.Forms;
    using Microsoft.FxCop.Sdk;

    /// <summary>
    /// Helper methods for the <see cref="ComponentInfo"/> and <see cref="ComponentDictionary"/> objects.
    /// </summary>
    public static class ComponentInfoHelper
    {
        #region Public Methods
        /// <summary>
        /// Gets the <see cref="Control.AccessibleName"/> property of the control.
        /// </summary>
        /// <param name="component">The component.</param>
        /// <returns>The <see cref="Control.AccessibleName"/> property of the control.</returns>
        public static string GetAccessibleName(this ComponentInfo component)
        {
            if (component == null)
            {
                throw new ArgumentNullException("component");
            }

            // If the AccessibleName is set for a control then that will be used.
            string accessibleName;
            component.TryGetValue<string>("AccessibleName", out accessibleName);

            return accessibleName;
        }

        /// <summary>
        /// Gets the <see cref="Control.AccessibleName"/> property of the control.
        /// </summary>
        /// <param name="componentDictionary">The component dictionary.</param>
        /// <param name="field">The control field.</param>
        /// <returns>The <see cref="Control.AccessibleName"/> property of the control.</returns>
        public static string GetAccessibleName(this ComponentDictionary componentDictionary, Field field)
        {
            if (componentDictionary == null)
            {
                throw new ArgumentNullException("componentDictionary");
            }

            // Get the control associated with the field, if any.
            ComponentInfo component;
            if (componentDictionary.TryGetValue(field, out component))
            {
                return component.GetAccessibleName();
            }

            return null;
        }

        /// <summary>
        /// Gets the bounds for the component.
        /// </summary>
        /// <param name="component">The component.</param>
        /// <returns>The bounds for the component.</returns>
        /// <remarks>
        /// If the component doesn't have both a Location and a Size value then a rectangle at position
        /// (int.MinValue, int.MinValue) with as size of (0, 0) is returned.
        /// </remarks>
        public static Rectangle GetBounds(this ComponentInfo component)
        {
            if (component == null)
            {
                throw new ArgumentNullException("component");
            }

            Point location;
            Size size;
            if (component.TryGetValue<Point>("Location", out location)
                && component.TryGetValue<Size>("Size", out size))
            {
                return new Rectangle(location, size);
            }

            return new Rectangle(int.MinValue, int.MinValue, 0, 0);
        }

        /// <summary>
        /// Determines whether the sibling component is near target component.
        /// </summary>
        /// <param name="targetComponent">The target component.</param>
        /// <param name="siblingComponent">The sibling component.</param>
        /// <returns><c>true</c> if the specified target component is near the sibling component.</returns>
        /// <remarks>
        /// Both components must have a <see cref="Control.Location"/> and <see cref="Control.Size"/>. The vertical center
        /// of the sibling must be within the vertical position of the target. The target is no more than 12 pixels to the
        /// right of the sibling.
        /// </remarks>
        public static bool IsNear(this ComponentInfo targetComponent, ComponentInfo siblingComponent)
        {
            // Fail if there isn't a Location and Size for both components.
            var targetBounds = targetComponent.GetBounds();
            var siblingBounds = siblingComponent.GetBounds();
            if ((targetBounds.X == int.MinValue) || (siblingBounds.X == int.MinValue))
            {
                return false;
            }

            // Is the vertical center of the sibling inside the target? If not assume it isn't "near".
            var testY = siblingBounds.Y + (siblingBounds.Height / 2);
            if ((testY < targetBounds.Y) || (testY > targetBounds.Y + targetBounds.Height))
            {
                return false;
            }

            // If the target control is more than 12 pixels to the right assume it isn't "near".
            var delta = targetBounds.X - (siblingBounds.X + siblingBounds.Width);
            return (delta >= -4) && (delta <= 12);
        }

        /// <summary>
        /// Gets the <see cref="Control.TabStop"/> property value.
        /// </summary>
        /// <param name="component">The component.</param>
        /// <returns>The <see cref="Control.TabStop"/> property value.</returns>
        public static bool IsTabStop(this ComponentInfo component)
        {
            if (component == null)
            {
                throw new ArgumentNullException("component");
            }

            // If this control has a TabStop property then return it.
            bool isTabStop;
            if (component.TryGetValue<bool>("TabStop", out isTabStop))
            {
                return isTabStop;
            }

            // The default value is true so it isn't set assume it is true.
            return true;
        }

        /// <summary>
        /// Gets the <see cref="Control.TabStop"/> property value.
        /// </summary>
        /// <param name="componentDictionary">The component dictionary.</param>
        /// <param name="field">The control field.</param>
        /// <returns>The <see cref="Control.TabStop"/> property value.</returns>
        public static bool IsTabStop(this ComponentDictionary componentDictionary, Field field)
        {
            if (componentDictionary == null)
            {
                throw new ArgumentNullException("componentDictionary");
            }

            ComponentInfo component;
            if (componentDictionary.TryGetValue(field, out component))
            {
                return component.IsTabStop();
            }

            return false;
        }
        #endregion Public Methods
    }
}